/**
 * 
 */
package edu.cornell.cs.lsi.projone.service;

import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.LogFactoryImpl;

import edu.cornell.cs.lsi.projone.common.LSIConstants;
import edu.cornell.cs.lsi.projone.common.LSIException;
import edu.cornell.cs.lsi.projone.common.util.AppUtil;
import edu.cornell.cs.lsi.projone.model.ServerData;

public class RPCClientService {

	private final static Log LOGGER = LogFactoryImpl
			.getLog(RPCClientService.class);

	private GroupMemberManager grpMgr = null;
	private int portNum = 0;
	private final long getTimeout;
	private final long putTimeout;
	private final long delTimeout;

	public RPCClientService() throws LSIException {

		grpMgr = new GroupMemberManager();

		portNum = AppUtil.getPort();

		LOGGER.info("RPC CLIENT: ----- listen port : " + portNum);

		getTimeout = 3000;
		putTimeout = 3000;
		delTimeout = 3000;
	}

	public String RpcClientGet(String sessionID, double versionNumber)
			throws LSIException {
		LOGGER.info("RpcClientGet(): method starts....");
		Random generator = new Random();
		int callID = AppUtil.getCallID();
		int cID;
		StringBuffer sb = new StringBuffer();
		sb.append(callID);
		sb.append(LSIConstants.PACKET_DATA_DELIMITER);
		sb.append("GET");
		sb.append(LSIConstants.PACKET_DATA_DELIMITER);
		sb.append(sessionID);
		sb.append(LSIConstants.PACKET_DATA_DELIMITER);
		sb.append(versionNumber);

		String outBuf = sb.toString();
		String inBuf = "";

		Collection<ServerData> tempList = grpMgr.getServerList();
		if (tempList == null || tempList.isEmpty()) {
			LOGGER.warn("RPC client: There are no servers in the database");
			return "";
		}
		List<ServerData> serverlist = new ArrayList<ServerData>();
		serverlist.addAll(tempList);
		int N = serverlist.size();
		try {
			for (int i = 0; i < N; i++) {
				int num = generator.nextInt(N);
				try {
					UDPService.sendPacket(outBuf, serverlist.get(num));
				} catch (UnknownHostException exp) {
					LOGGER.warn("The host is not reachable: "
							+ serverlist.get(num));
				}
			}
		} catch (LSIException e) {
			e.printStackTrace();
		}

		do {
			try {
				inBuf = UDPService.recvPacket(portNum, (int) getTimeout);
			} catch (SocketTimeoutException e) {
				e.printStackTrace();
				break;
			}
			cID = Integer.parseInt(inBuf
					.split(LSIConstants.PACKET_DATA_DELIMITER)[0]);
		} while (cID != callID);

		LOGGER.info("RpcClientGet(): method ends......");
		return inBuf;
	}

	public String[] RpcClientPut(String sessionID, double versionNumber,
			String sessionCreationTime, int counter, String sessionVal, String sv)
			throws LSIException {
		LOGGER.info("RpcClientPut(): method strats......");
		Random generator = new Random();
		int callID = AppUtil.getCallID();
		int count = 0;
		StringBuffer sb = new StringBuffer();
		StringBuffer servers = new StringBuffer();
		sb.append(callID);
		sb.append(LSIConstants.PACKET_DATA_DELIMITER);
		sb.append("PUT");
		sb.append(LSIConstants.PACKET_DATA_DELIMITER);
		sb.append(sessionID);
		sb.append(LSIConstants.PACKET_DATA_DELIMITER);
		sb.append(versionNumber);
		sb.append(LSIConstants.PACKET_DATA_DELIMITER);
		sb.append(sessionCreationTime);
		sb.append(LSIConstants.PACKET_DATA_DELIMITER);
		sb.append(counter);
		sb.append(LSIConstants.PACKET_DATA_DELIMITER);
		sb.append(sessionVal);

		String outBuf = sb.toString();
		String inBuf = "";

		Collection<ServerData> templist = grpMgr.getServerList();
		List<ServerData> serverlist = new ArrayList<ServerData>();
		serverlist.addAll(templist);
		int N = serverlist.size();
		int NQ = N / 2 + 1;
		LOGGER.info("RPC PUT :------------------------- server list size: "
				+ serverlist.size());
		try {
			for (int i = 0; i < N; i++) {
				int num = generator.nextInt(N);
				try {
					UDPService.sendPacket(outBuf, serverlist.get(num));
				} catch (UnknownHostException exp) {
					LOGGER.warn("Host is not reachabel: " + serverlist.get(num));
				}
			}
		} catch (LSIException e) {
			e.printStackTrace();
		}

		do {
			count++;
			try {
				inBuf = UDPService.recvPacket(portNum, (int) putTimeout);
			} catch (SocketTimeoutException e) {
				e.printStackTrace();
				break;
			}
			servers.append(inBuf.split(LSIConstants.PACKET_DATA_DELIMITER)[2]); // IP
			servers.append(LSIConstants.PACKET_DATA_DELIMITER);
			servers.append(inBuf.split(LSIConstants.PACKET_DATA_DELIMITER)[3]); // Port
			servers.append("_");
		} while (count < NQ);

		String st = servers.toString();
		String[] retlist = new String[st.length()];
		retlist = st.split("_");

		LOGGER.info("RpcClientPut(): method ends......");
		
		String[] svlist = sv.split("_");
		for(int i=0; i<svlist.length;i++)
		{
			if(!st.contains(svlist[i])){
				RpcClientDel(sessionID,svlist[i]);
			}
		}
		
		return retlist;

	}

	public void RpcClientDel(String sessionID, String servers)
			throws LSIException {
		LOGGER.info("RpcClientDel(): method starts......");
		if (sessionID == null || StringUtils.isEmpty(sessionID)
				|| servers == null || StringUtils.isEmpty(servers)) {
			return;
		}
		System.out.println("=============== " + servers);
		String[] s = servers.split("_");
		if (s == null || s.length < 2) {
			LOGGER.info("RpcClientDel(): returning as the incoming server list is empty.");
			return;
		}
		int numservers = s.length;
		int count = 0;
		ServerData sd;
		String outBuf = "";
		String inBuf = "";

		StringBuffer sb = new StringBuffer();
		sb.append(" ");
		sb.append(LSIConstants.PACKET_DATA_DELIMITER);
		sb.append("DEL");
		sb.append(LSIConstants.PACKET_DATA_DELIMITER);
		sb.append(sessionID);
		outBuf = sb.toString();

		try {
			for (int i = 0; i < numservers; i++) {
				sd = new ServerData(
						s[i].split(LSIConstants.PACKET_DATA_DELIMITER)[0],
						Integer.parseInt(s[i]
								.split(LSIConstants.PACKET_DATA_DELIMITER)[1]));
				try {
					UDPService.sendPacket(outBuf, sd);
				} catch (UnknownHostException exp) {
					LOGGER.warn("Host is not reachabel: ");
				}
			}
		} catch (LSIException e) {
			e.printStackTrace();
		}

		do {
			count++;
			try {
				inBuf = UDPService.recvPacket(portNum, (int) delTimeout);
			} catch (SocketTimeoutException e) {
				e.printStackTrace();
				break;
			}

		} while (count < numservers);

		LOGGER.info("RpcClientDel(): method ends......");
	}

}
